package cheme.Interpreter.AST.Variable;

import cheme.Interpreter.AST.Interfaces.IAstExpression;
import cheme.Interpreter.AST.Interfaces.IAstType;
import cheme.Interpreter.AST.Interfaces.IAstVariable;
import cheme.Interpreter.AST.Interfaces.IAstVariableReference;
import cheme.Interpreter.AST.Interfaces.IObserver;
import cheme.Interpreter.AST.Interfaces.ISubject;
import cheme.Interpreter.AST.Interfaces.SubjectCore;
import cheme.Interpreter.AST.SymbolTable.SymbolTable;

public class AstVariableReference implements IAstVariableReference
{
	static int id = 0;
	int myId = id++;
	
	private SymbolTable symbolTable = null;
	private IAstVariable referrent = null;
	
	private SubjectCore subjectCore = new SubjectCore(this);

	public AstVariableReference(SymbolTable symbolTable, String identifier)
	{
		super();
		this.symbolTable = symbolTable;
		
		resolve(identifier);
	}

	/*
	 * When a refrence is made to a varaible by name, it is looked for in the 
	 * symbol table. If it is there an obersver reference is established, if it 
	 * is not there a new variable is added with uninitialized data and a state 
	 * of 'inferred declaration.'
	 */
	private void resolve(String identifier)
	{
		referrent = symbolTable.get(identifier.toString());
		
		if(null == referrent)
		{
			//Add that dummy!
			referrent = 
 				new AstVariable(
						new AstIdentifier(identifier), 
						IAstExpression.uninitialized);
			
			referrent.setDeclarationState(IAstVariable.DeclarationState.InferredDeclaration);
			//referrent.setValueationState(IAstVariable.ValueationState.Unknown);
			
			referrent.attach(this);
			
			symbolTable.add(referrent);
		}
		
		//TODO: The variable is guaranteed to exist, soooo.... what exactly needs to 'observe' this guy I have in my hand?
	}

	public SymbolTable getSymbolTable()
	{
		return symbolTable;
	}

	public void setSymbolTable(SymbolTable symbolTable)
	{
		this.symbolTable = symbolTable;
	}

	protected IAstVariable getReferrent()
	{
		return referrent;
	}

	protected void setReferrent(IAstVariable referrent)
	{
		this.referrent = referrent;
	}
	
	public IAstType getType()
	{
		if(null != getReferrent())
		{
			return getReferrent().getType();
		}
		
		return IAstType.nullType;
	}

	public ValueationState getValueationState()
	{
		return getReferrent().getValueationState();
	}

	public double evaluate()
	{
		//lookup in table, eval the referent
		if(null != getReferrent())
		{
			return getReferrent().evaluate();
		}
		
		return 0;
	}
	
	@Override
	public String toString()
	{
		String string = getReferrent().getIdentifier().toString();
			
		if(getReferrent().isUnknown())
		{
			string =  "[" + string + "]";
		}
			
		if(IAstVariable.DeclarationState.InferredDeclaration == getReferrent().getDeclarationState())
		{
			string =  "{" + string + "}";
		}
		
		return string;
	}

	public void notify(ISubject s)
	{
		notifyObservers();
	}

	public void attach(IObserver o)
	{
		subjectCore.attach(o);
	}

	public void detach(IObserver o)
	{
		subjectCore.detach(o);
	}

	public void notifyObservers()
	{
		subjectCore.notifyObservers();
	}

	public boolean isUnknown()
	{
		if(null != getReferrent())
		{
			return getReferrent().isUnknown();
		}
		return false;
	}
}
